/*   Histogramm constriction and research tasks:
1   -   construct one channel histogram for input model (default)
2   -   gaussian fuiltering
3   -   multi-demensional histogram (default and gradient, using sobel)
4   -   material classification using default histogram
5   -   color transformation function using histogram classes
*/
#include <string>
#include <iostream>
#include <algorithm>
#include "tclap\CmdLine.h"
#include "matrix3D.hxx"
#include "histogram.hxx"
#include "ptr.hxx"

using std::string;
using namespace TCLAP;

int main(int argc, char** argv)
{
    try {
        CmdLine cmd("Command line arguments for Histogram\n", ' ', "0.1-nightly");

        UnlabeledValueArg<char> sobelDir("sobel-dir", "The derivative direction [x of y or z]", false, 'x',
            "The x by default");
        cmd.add( sobelDir );

        ValueArg<double> histTr("g","hist-tr", "The thrashold for histogram classification",false, 0.2,
            "The 0.2 by default");
        cmd.add( histTr );

        SwitchArg sobel("v","sobel-derivative","Create calculate sobel derivative for input matrix", false);
        cmd.add( sobel );

        ValueArg<std::string> materialPath("s","source-material",
            "Input scalar material in .bin format",false,"random",
            "The string with path to material file");
        cmd.add( materialPath );

        ValueArg<int> cudaWarps("w","cuda-warps",
            "Humber of warps in one thread block ",false, 12,
            "Warp size mast ne bitween 2 and 32");
        cmd.add( cudaWarps );

        ValueArg<int> xModelSize("x","x-size",
            "Set model size for x coordinate (the value must be multilpy by 32)", false,32,
            "The default value is 32. Paremeter used if you randomly generate matrix");
        cmd.add( xModelSize );

        ValueArg<int> yModelSize("y","y-size",
            "Set model size for y coordinate (the value must be multilpy by 32)", false,32,
            "The default value is 32. Paremeter used if you randomly generate matrix");
        cmd.add( yModelSize );

        ValueArg<int> zModelSize("z","z-size",
            "Set model size for z coordinate (the value must be multilpy by 32)", false,32,
            "The default value is 32. Paremeter used if you randomly generate matrix");
        cmd.add( zModelSize );

        //user actions:
        SwitchArg histogramOnly("o","histogram","Create one channel histogramm for input material", false);
        cmd.add( histogramOnly );

        SwitchArg multihistogram("m","multi","Create 2-dimensional histogram for voxel material content and gradient", false);
        cmd.add( multihistogram );

        SwitchArg classify("c","classify","Classify input material by hispogram", false);
        cmd.add( classify );

        SwitchArg transform("t","transform","Transform material by classification", false);
        cmd.add( transform );

        cmd.parse( argc, argv );

        Matrix3D material = (materialPath.getValue() != "random")? 
            Matrix::parse(materialPath.getValue()) :
        Matrix::generateUniform(xModelSize.getValue(),yModelSize.getValue(),zModelSize.getValue());

        if(histogramOnly.getValue())
        {
            Histogram histogram = material.histogram(cudaWarps.getValue());
            std::cout << histogram;
            return 0;
        }

        if(sobel.getValue())
        {
             Matrix3D deriv = material.sobel(Matrix3D::DX);
             std::cout << "Sobel finished" << std::endl;
        }

        if(multihistogram.getValue())
        {
            Histogram mhistogram = material.histogram(cudaWarps.getValue(), Histogram::GRADIENT | Histogram::DESTRIBUTION);
            std::cout << mhistogram;
            return 0;
        }

        if(classify.getValue())
        {
            material.smooth();
            Histogram histogram = material.histogram(cudaWarps.getValue(), Histogram::DESTRIBUTION);
            std::vector<int> classes = histogram.classify(histTr.getValue());
            if(transform.getValue())
            {
                material.transform(classes);
            }
        }
        material.store("some.bin");

   } catch (ArgException &e)
    {
        std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    }
}
